package com.ruyiadmin.springboot.controller.business.module;

import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.ruyiadmin.springboot.common.annotations.core.AllowAnonymous;
import com.ruyiadmin.springboot.common.annotations.system.Log;
import com.ruyiadmin.springboot.common.annotations.system.Permission;
import com.ruyiadmin.springboot.common.awares.core.RuYiAdminContextAware;
import com.ruyiadmin.springboot.common.beans.system.JwtSettings;
import com.ruyiadmin.springboot.common.beans.system.SystemConfig;
import com.ruyiadmin.springboot.common.beans.system.SystemRedisConfig;
import com.ruyiadmin.springboot.common.components.core.RuYiRedisComponent;
import com.ruyiadmin.springboot.common.core.business.enums.DeletionType;
import com.ruyiadmin.springboot.common.core.business.enums.OperationType;
import com.ruyiadmin.springboot.common.core.business.enums.YesNo;
import com.ruyiadmin.springboot.common.core.system.entities.ActionResult;
import com.ruyiadmin.springboot.common.core.system.entities.QueryCondition;
import com.ruyiadmin.springboot.common.core.system.entities.QueryResult;
import com.ruyiadmin.springboot.common.events.system.SysLogEvent;
import com.ruyiadmin.springboot.common.exceptions.RuYiAdminCustomException;
import com.ruyiadmin.springboot.common.utils.core.RuYiAesUtil;
import com.ruyiadmin.springboot.common.utils.core.RuYiRsaUtil;
import com.ruyiadmin.springboot.domain.dto.business.module.BizAccountDTO;
import com.ruyiadmin.springboot.domain.dto.business.module.BizUserDTO;
import com.ruyiadmin.springboot.domain.dto.business.module.BizUserModuleDTO;
import com.ruyiadmin.springboot.domain.dto.system.LoginDTO;
import com.ruyiadmin.springboot.domain.entity.business.module.BizModule;
import com.ruyiadmin.springboot.domain.entity.business.module.BizUser;
import com.ruyiadmin.springboot.domain.entity.business.module.BizUserModule;
import com.ruyiadmin.springboot.domain.entity.system.SysLog;
import com.ruyiadmin.springboot.service.iservices.business.module.IBizModuleService;
import com.ruyiadmin.springboot.service.iservices.business.module.IBizUserModuleService;
import com.ruyiadmin.springboot.service.iservices.business.module.IBizUserService;
import eu.bitwalker.useragentutils.UserAgent;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;

/**
 * <p>
 * 模块用户表 前端控制器
 * </p>
 *
 * @author RuYiAdmin
 * @since 2022-07-12
 */
@RestController
@RequestMapping("/BizUserManagement")
@Api(tags = "系统模块用户管理服务")
@RequiredArgsConstructor
public class BizUserController {

    //region 管理服务私有属性

    private final SystemRedisConfig systemRedisConfig;
    private final SystemConfig systemConfig;
    private final JwtSettings jwtSettings;
    private final RuYiRedisComponent redisUtils;
    private final IBizModuleService bizModuleService;
    private final IBizUserService bizUserService;
    private final IBizUserModuleService bizUserModuleService;

    //endregion

    //region 查询离态用户列表

    @PostMapping("/Post")
    @ApiOperation(value = "查询离态用户列表")
    @Log(OperationType = OperationType.QueryList)
    @Permission(permission = "user:nonmodule:list")
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public QueryResult<BizUserModuleDTO> queryByPage(@RequestBody QueryCondition queryCondition)
            throws ExecutionException, InterruptedException {
        CompletableFuture<QueryResult<BizUserModuleDTO>> future = CompletableFuture.supplyAsync(() ->
                this.bizUserService.queryUserNonModule(queryCondition));
        return future.get();
    }

    //endregion

    //region 同步新增模块用户

    @PostMapping("/Add")
    @ApiOperation(value = "同步新增模块用户")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult add(@RequestBody @Valid BizUserDTO bizUserDTO) throws Exception {
        bizUserDTO.setUserPassword(RuYiRsaUtil.decrypt(bizUserDTO.getUserPassword(),
                this.systemConfig.getRsaPrivateKey()));
        String[] array = bizUserDTO.getUserPassword().split("_");
        String password = array[0];
        String token = array[1];

        String key = this.systemRedisConfig.getSynchronizationPattern() + token;
        Object value = this.redisUtils.get(key);
        BizAccountDTO accountDTO = JSON.parseObject(value.toString(), BizAccountDTO.class);
        if (accountDTO != null && accountDTO.getToken().equals(token)) {
            BizModule module = this.bizModuleService.getById(bizUserDTO.getModuleId());
            bizUserDTO.setUserLogonName(RuYiRsaUtil.decrypt(bizUserDTO.getUserLogonName(),
                    this.systemConfig.getRsaPrivateKey()));
            bizUserDTO.setUserLogonName(bizUserDTO.getUserLogonName().split("_")[0]);
            String userLogonName = StrUtil.format("{}_{}",
                    module.getModuleShortNameEn(), bizUserDTO.getUserLogonName());
            List<BizUser> bizUsers = this.bizUserService.queryAllBizUsers();
            long count = bizUsers.stream().filter(t -> t.getUserLogonName().equals(userLogonName)).count();
            if (count > 0) {
                throw new RuYiAdminCustomException("logon name has existed");
            }

            String defaultPassword = this.systemConfig.getDefaultPassword();
            String aesKey = this.systemConfig.getAesKey();

            //region 新增模块业务用户

            BizUser bizUser = new BizUser();
            bizUser.setUserLogonName(userLogonName);
            bizUser.setUserDisplayName(bizUserDTO.getUserDisplayName());
            String salt = UUID.randomUUID().toString();
            bizUser.setSalt(salt);
            bizUser.setUserPassword(RuYiAesUtil.encrypt(defaultPassword + salt, aesKey));
            bizUser.setTelephone(bizUserDTO.getTelephone());
            bizUser.setMobilephone(bizUserDTO.getMobilephone());
            bizUser.setEmail(bizUserDTO.getEmail());
            bizUser.setSex(bizUserDTO.getSex());
            bizUser.setIsEnabled(YesNo.YES.ordinal());
            bizUser.setIsdel(DeletionType.Undeleted.ordinal());
            bizUser.setCreator(accountDTO.getId());
            bizUser.setCreateTime(LocalDateTimeUtil.now());
            bizUser.setModifier(accountDTO.getId());
            bizUser.setModifyTime(LocalDateTimeUtil.now());
            bizUser.setVersionId(UUID.randomUUID().toString());
            this.bizUserService.save(bizUser);

            //endregion

            //region 新增模块与用户关系

            BizUserModule userModule = new BizUserModule();
            userModule.setUserId(bizUser.getId());
            userModule.setModuleId(bizUserDTO.getModuleId());
            userModule.setUserModuleLogonName(bizUserDTO.getUserLogonName());
            userModule.setUserModulePassword(password);
            userModule.setIsdel(DeletionType.Undeleted.ordinal());
            userModule.setCreator(accountDTO.getId());
            userModule.setCreateTime(LocalDateTimeUtil.now());
            userModule.setModifier(accountDTO.getId());
            userModule.setModifyTime(LocalDateTimeUtil.now());
            userModule.setVersionId(UUID.randomUUID().toString());
            this.bizUserModuleService.save(userModule);

            //endregion

            //region 记录同步新增用户日志

            //获取请求url,ip,httpMethod
            HttpServletRequest request = ((ServletRequestAttributes) Objects
                    .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
            String clientType = userAgent.getOperatingSystem().getDeviceType().toString();//客户端类型  手机、电脑、平板
            String os = userAgent.getOperatingSystem().getName();//操作系统类型
            String browser = userAgent.getBrowser().toString();//浏览器类型

            SysLog sysLog = new SysLog();

            sysLog.setId(UUID.randomUUID().toString());
            sysLog.setUserId(accountDTO.getId());
            sysLog.setUserName(accountDTO.getUserDisplayName() + "/" + accountDTO.getUserLogonName());
            sysLog.setOrgId(module.getId());
            sysLog.setOrgName(module.getModuleShortName());
            sysLog.setSystem(clientType + "，" + os);
            sysLog.setBrowser(browser);
            sysLog.setIp(ServletUtil.getClientIP(request));
            sysLog.setRequestMethod(request.getMethod());
            sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
            sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
            sysLog.setResult(StringUtils.EMPTY);
            sysLog.setOldValue(StringUtils.EMPTY);
            sysLog.setNewValue(StringUtils.EMPTY);
            sysLog.setRemark(sysLog.getUserName() + "于" + LocalDateTimeUtil.now()
                    + "访问了" + sysLog.getRequestUrl() + "接口");
            sysLog.setIsdel(DeletionType.Undeleted.ordinal());
            sysLog.setCreator(accountDTO.getId());
            sysLog.setCreateTime(LocalDateTimeUtil.now());
            sysLog.setModifier(accountDTO.getId());
            sysLog.setModifyTime(LocalDateTimeUtil.now());
            sysLog.setVersionId(UUID.randomUUID().toString());
            sysLog.setOperationType(OperationType.SyncAddUser.ordinal());

            // 发送异步日志事件
            RuYiAdminContextAware.publishEvent(new SysLogEvent(sysLog));

            //endregion

            return ActionResult.ok();
        }
        throw new RuYiAdminCustomException("token is invalid");
    }

    //endregion

    //region 同步编辑模块用户

    @PutMapping("/Put")
    @ApiOperation(value = "同步编辑模块用户")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult edit(@RequestBody @Valid BizUserDTO bizUserDTO) throws Exception {
        bizUserDTO.setUserPassword(RuYiRsaUtil.decrypt(bizUserDTO.getUserPassword(),
                this.systemConfig.getRsaPrivateKey()));
        String[] array = bizUserDTO.getUserPassword().split("_");
        String password = array[0];
        String token = array[1];

        String key = this.systemRedisConfig.getSynchronizationPattern() + token;
        Object value = this.redisUtils.get(key);
        BizAccountDTO accountDTO = JSON.parseObject(value.toString(), BizAccountDTO.class);
        if (accountDTO != null && accountDTO.getToken().equals(token)) {
            BizModule module = this.bizModuleService.getById(bizUserDTO.getModuleId());
            bizUserDTO.setUserLogonName(RuYiRsaUtil.decrypt(bizUserDTO.getUserLogonName(),
                    this.systemConfig.getRsaPrivateKey()));
            bizUserDTO.setUserLogonName(bizUserDTO.getUserLogonName().split("_")[0]);
            String userLogonName = StrUtil.format("{}_{}",
                    module.getModuleShortNameEn(), bizUserDTO.getUserLogonName());
            List<BizUser> bizUsers = this.bizUserService.list();
            bizUsers = bizUsers.stream().filter(t -> t.getUserLogonName().equals(userLogonName))
                    .collect(Collectors.toList());
            if (bizUsers.size() > 0) {

                //region 同步编辑模块用户

                BizUser bizUser = bizUsers.get(0);
                bizUser.setUserDisplayName(bizUserDTO.getUserDisplayName());
                bizUser.setTelephone(bizUserDTO.getTelephone());
                bizUser.setMobilephone(bizUserDTO.getMobilephone());
                bizUser.setEmail(bizUserDTO.getEmail());
                bizUser.setSex(bizUserDTO.getSex());
                bizUser.setModifier(accountDTO.getId());
                bizUser.setModifyTime(LocalDateTimeUtil.now());
                bizUser.setVersionId(UUID.randomUUID().toString());
                this.bizUserService.updateById(bizUser);

                //endregion

                //region 同步编辑模块与用户关系

                List<BizUserModule> userModules = this.bizUserModuleService.list().stream()
                        .filter(t -> t.getUserId().equals(bizUser.getId()))
                        .filter(t -> t.getModuleId().equals(bizUserDTO.getModuleId()))
                        .collect(Collectors.toList());
                if (userModules.size() > 0) {
                    BizUserModule userModule = userModules.get(0);
                    userModule.setUserModulePassword(password);
                    userModule.setModifier(accountDTO.getId());
                    userModule.setModifyTime(LocalDateTimeUtil.now());
                    userModule.setVersionId(UUID.randomUUID().toString());
                    this.bizUserModuleService.updateById(userModule);
                }

                //endregion

                //region 记录同步编辑用户日志

                //获取请求url,ip,httpMethod
                HttpServletRequest request = ((ServletRequestAttributes) Objects
                        .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

                UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
                String clientType = userAgent.getOperatingSystem().getDeviceType().toString();//客户端类型  手机、电脑、平板
                String os = userAgent.getOperatingSystem().getName();//操作系统类型
                String browser = userAgent.getBrowser().toString();//浏览器类型

                SysLog sysLog = new SysLog();

                sysLog.setId(UUID.randomUUID().toString());
                sysLog.setUserId(accountDTO.getId());
                sysLog.setUserName(accountDTO.getUserDisplayName() + "/" + accountDTO.getUserLogonName());
                sysLog.setOrgId(module.getId());
                sysLog.setOrgName(module.getModuleShortName());
                sysLog.setSystem(clientType + "，" + os);
                sysLog.setBrowser(browser);
                sysLog.setIp(ServletUtil.getClientIP(request));
                sysLog.setRequestMethod(request.getMethod());
                sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
                sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
                sysLog.setResult(StringUtils.EMPTY);
                sysLog.setOldValue(StringUtils.EMPTY);
                sysLog.setNewValue(StringUtils.EMPTY);
                sysLog.setRemark(sysLog.getUserName() + "于" + LocalDateTimeUtil.now()
                        + "访问了" + sysLog.getRequestUrl() + "接口");
                sysLog.setIsdel(DeletionType.Undeleted.ordinal());
                sysLog.setCreator(accountDTO.getId());
                sysLog.setCreateTime(LocalDateTimeUtil.now());
                sysLog.setModifier(accountDTO.getId());
                sysLog.setModifyTime(LocalDateTimeUtil.now());
                sysLog.setVersionId(UUID.randomUUID().toString());
                sysLog.setOperationType(OperationType.SyncEditUser.ordinal());

                // 发送异步日志事件
                RuYiAdminContextAware.publishEvent(new SysLogEvent(sysLog));

                //endregion

                return ActionResult.ok();
            } else {
                throw new RuYiAdminCustomException("not found");
            }
        }
        throw new RuYiAdminCustomException("token is invalid");
    }

    //endregion

    //region 同步删除模块用户

    @PostMapping("/DeleteRange")
    @ApiOperation(value = "同步删除模块用户")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult deleteRange(@RequestBody @Valid BizUserDTO bizUserDTO) throws Exception {
        bizUserDTO.setUserLogonName(RuYiRsaUtil.decrypt(bizUserDTO.getUserLogonName(),
                this.systemConfig.getRsaPrivateKey()));
        String[] array = bizUserDTO.getUserLogonName().split("_");
        String token = array[1];

        String key = this.systemRedisConfig.getSynchronizationPattern() + token;
        Object value = this.redisUtils.get(key);
        BizAccountDTO accountDTO = JSON.parseObject(value.toString(), BizAccountDTO.class);
        if (accountDTO != null && accountDTO.getToken().equals(token)) {
            String[] logonNames = array[0].split(",");
            BizModule module = this.bizModuleService.getById(bizUserDTO.getModuleId());
            List<BizUser> bizUsers = this.bizUserService.list();

            List<String> ids = new ArrayList<>();
            for (String logonName : logonNames) {
                String userLogonName = StrUtil.format("{}_{}",
                        module.getModuleShortNameEn(), logonName);
                List<BizUser> users = bizUsers.stream()
                        .filter(t -> t.getUserLogonName().equals(userLogonName))
                        .collect(Collectors.toList());
                if (users.size() > 0) {
                    BizUser bizUser = users.get(0);
                    List<BizUserModule> userModules = this.bizUserModuleService.list().stream()
                            .filter(t -> t.getUserId().equals(bizUser.getId()))
                            .filter(t -> t.getModuleId().equals(bizUserDTO.getModuleId()))
                            .collect(Collectors.toList());
                    if (userModules.size() > 0) {
                        BizUserModule userModule = userModules.get(0);
                        ids.add(userModule.getId());
                    }
                } else {
                    throw new RuYiAdminCustomException(logonName + " is not existed");
                }
            }
            //删除授权关系
            this.bizUserModuleService.removeByIds(new ArrayList<>(ids));

            //region 记录同步删除用户日志

            //获取请求url,ip,httpMethod
            HttpServletRequest request = ((ServletRequestAttributes) Objects
                    .requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();

            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("user-agent"));
            String clientType = userAgent.getOperatingSystem().getDeviceType().toString();//客户端类型  手机、电脑、平板
            String os = userAgent.getOperatingSystem().getName();//操作系统类型
            String browser = userAgent.getBrowser().toString();//浏览器类型

            SysLog sysLog = new SysLog();

            sysLog.setId(UUID.randomUUID().toString());
            sysLog.setUserId(accountDTO.getId());
            sysLog.setUserName(accountDTO.getUserDisplayName() + "/" + accountDTO.getUserLogonName());
            sysLog.setOrgId(module.getId());
            sysLog.setOrgName(module.getModuleShortName());
            sysLog.setSystem(clientType + "，" + os);
            sysLog.setBrowser(browser);
            sysLog.setIp(ServletUtil.getClientIP(request));
            sysLog.setRequestMethod(request.getMethod());
            sysLog.setRequestUrl(URLUtil.getPath(request.getRequestURI()));
            sysLog.setParams(HttpUtil.toParams(request.getParameterMap()));
            sysLog.setResult(StringUtils.EMPTY);
            sysLog.setOldValue(StringUtils.EMPTY);
            sysLog.setNewValue(StringUtils.EMPTY);
            sysLog.setRemark(sysLog.getUserName() + "于" + LocalDateTimeUtil.now()
                    + "访问了" + sysLog.getRequestUrl() + "接口");
            sysLog.setIsdel(DeletionType.Undeleted.ordinal());
            sysLog.setCreator(accountDTO.getId());
            sysLog.setCreateTime(LocalDateTimeUtil.now());
            sysLog.setModifier(accountDTO.getId());
            sysLog.setModifyTime(LocalDateTimeUtil.now());
            sysLog.setVersionId(UUID.randomUUID().toString());
            sysLog.setOperationType(OperationType.SyncDeleteUser.ordinal());

            // 发送异步日志事件
            RuYiAdminContextAware.publishEvent(new SysLogEvent(sysLog));

            //endregion

            return ActionResult.ok();
        } else {
            throw new RuYiAdminCustomException("not found");
        }
    }

    //endregion

    //region 匿名获取盐份服务

    @GetMapping("/GetSalt")
    @ApiOperation(value = "匿名获取盐份服务")
    @AllowAnonymous
    public ActionResult getSalt() {
        String salt = UUID.randomUUID().toString();
        this.redisUtils.set(salt, salt, jwtSettings.getSaltExpiration());
        return ActionResult.success(salt);
    }

    //endregion

    //region 匿名获取同步口令

    @PostMapping("/GetToken")
    @ApiOperation(value = "匿名获取同步口令")
    @AllowAnonymous
    @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class)
    public ActionResult getToken(@RequestBody @Valid LoginDTO loginDTO) throws Exception {
        return this.bizUserModuleService.getToken(loginDTO);
    }

    //endregion

}
